package com.allwees.bs.c.module.user.service.impl;

import com.allwees.bs.c.module.ops.service.UserCouponService;
import com.allwees.bs.c.module.user.dao.UserSignInLogDao;
import com.allwees.bs.c.module.user.entity.UserCouponEntity;
import com.allwees.bs.c.module.user.entity.UserSignInLogEntity;
import com.allwees.bs.c.module.user.service.UserSignInLogService;
import com.allwees.bs.c.module.user.vo.UserSignInLogVo;
import com.allwees.bs.core.model.coupon.constant.ECouponUseType;
import com.allwees.bs.core.model.user.constant.ESignInCycleType;
import com.allwees.core.common.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
import java.util.List;


/**
 * @author mouhaining
 * @since 2020-05-20 9:30
 */
@Slf4j
@Service
public class UserSignInLogServiceImpl implements UserSignInLogService {

    @Value("${system.params.user.signIn.cycleDays:30}")
    private int cycleDays;

    @Value("${system.params.user.signIn.awardDays:7}")
    private int awardDays;

    @Resource
    private UserSignInLogDao userSignInLogDao;

    @Autowired
    private UserCouponService userCouponService;

    @Override
    public int countNormalEndCycle(String userUuid) {
        return userSignInLogDao.countNormalEndCycle(userUuid, ESignInCycleType.END.name());
    }

    @Override
    public UserSignInLogVo signIn(String userUuid) {
        log.info("===> user signin,userUuid:{}",userUuid);
        UserSignInLogVo logVo = new UserSignInLogVo();
        UserSignInLogEntity last = userSignInLogDao.getLast(userUuid);
        Date today = new Date();

        //周期长度(天)
        final int cycleDays = this.cycleDays;
        //历史首次登录
        if(last == null){
            saveSigninLog(userUuid, today,ESignInCycleType.IN);
            logVo.setSignedIn(false);
            logVo.setSignedTimes(1);
            logVo.setCompletedAt(DateUtil.truncateToDay(today, cycleDays));
            return logVo;
        }
        //一个周期内应签到次数(每天一次去重)
        final int rewardSignCount = this.awardDays;
        int limit = rewardSignCount - 1;
        boolean todaySigned = last.isTodaySigned();
        logVo.setSignedIn(todaySigned);
        //==================今日非首次签到==================
        if(todaySigned) {
            if(last.getCycleType() == ESignInCycleType.END){
                logVo.setCompletedAt(DateUtil.truncateToDay(today));
                logVo.setSignedTimes(rewardSignCount);
            } else {
                List<UserSignInLogEntity> latestLogs = userSignInLogDao.queryByLimit(userUuid, limit);
                UserSignInLogEntity endCycle = latestLogs.stream().filter(log -> log.getCycleType() == ESignInCycleType.END).findFirst().orElse(null);
                Collections.sort(latestLogs);
                UserSignInLogEntity firstInOneCycle;
                if (endCycle == null) {
                    firstInOneCycle = latestLogs.get(0);
                    logVo.setSignedTimes(latestLogs.size());
                } else {
                    int index = latestLogs.indexOf(endCycle) + 1;
                    firstInOneCycle = latestLogs.get(index);
                    logVo.setSignedTimes(latestLogs.size() - index);
                }
                logVo.setCompletedAt(DateUtil.truncateToDay(firstInOneCycle.getCreatedAt(), cycleDays));
            }
            return logVo;
        }

        //==================今日首次签到==================
        //除去本次签到，前面的(rewardSignCount - 1)次签到,结果按由近及远排序
        List<UserSignInLogEntity> latestLogs = userSignInLogDao.queryByLimit(userUuid, limit);
        //离现在最近的一次END记录,之后再按由远及近排序
        UserSignInLogEntity endCycle = latestLogs.stream().filter(log -> log.getCycleType() == ESignInCycleType.END).findFirst().orElse(null);
        Collections.sort(latestLogs);
        int signinTimes = latestLogs.size();
        UserSignInLogEntity firstInOneCycle;
        UserSignInLogEntity lastInOneCycle;

        //----------------最近signinTimes次签到里没有完整周期----------------
        if(endCycle == null){
            firstInOneCycle = latestLogs.get(0);
            lastInOneCycle = latestLogs.get(signinTimes - 1);
            //除去本次签到,本周期内剩余应签到次数
            int leftTimes = rewardSignCount - signinTimes - 1;
            //本周期内，剩余可签到天数少于应签到天数，则本次签到是一个新周期的开始
            if(startNewCycle(firstInOneCycle,lastInOneCycle,leftTimes,cycleDays,userUuid,logVo,today)){
                return logVo;
            }

            //本次签到刚好满rewardSignCount次
            if(leftTimes == 0){
                saveSigninLog(userUuid, today,ESignInCycleType.END);
                int endCycleCount = countNormalEndCycle(userUuid);
                ECouponUseType couponUseType = endCycleCount == 1 ? ECouponUseType.SYSTEM_SING_FIRST : ECouponUseType.SYSTEM_SING;
                UserCouponEntity couponEntity = userCouponService.signIn(userUuid, couponUseType);
                logVo.setCompletedAt(DateUtil.truncateToDay(today));
                logVo.setUserCoupon(couponEntity);
            } else {//普通签到
                saveSigninLog(userUuid, today,ESignInCycleType.IN);
                logVo.setCompletedAt(DateUtil.truncateToDay(firstInOneCycle.getCreatedAt(), cycleDays));
            }
            logVo.setSignedTimes(signinTimes + 1);
            return logVo;
        }

        //----------------最近signinTimes次签到里有完整周期----------------
        int endCycleIndex = latestLogs.indexOf(endCycle);
        boolean isLastInList = (endCycleIndex == (signinTimes - 1));
        //本周期第一次登录
        if(isLastInList){
            saveSigninLog(userUuid, today,ESignInCycleType.IN);
            logVo.setSignedTimes(1);
            logVo.setCompletedAt(DateUtil.truncateToDay(today));
            return logVo;
        }

        //本周期内非第一次登录
        firstInOneCycle = latestLogs.get(endCycleIndex + 1);
        int calculatedSigninTimes = signinTimes - endCycleIndex - 1;
        lastInOneCycle = latestLogs.get(signinTimes - 1);
        //除去本次签到,本周期内剩余应签到次数
        int leftTimes = rewardSignCount - calculatedSigninTimes - 1;
        //本周期内，剩余可签到天数少于应签到天数，则本次签到是一个新周期的开始
        if(startNewCycle(firstInOneCycle,lastInOneCycle,leftTimes,cycleDays,userUuid,logVo,today)){
            return logVo;
        }

        //普通签到
        saveSigninLog(userUuid, today,ESignInCycleType.IN);
        logVo.setSignedTimes(calculatedSigninTimes + 1);
        logVo.setCompletedAt(DateUtil.truncateToDay(firstInOneCycle.getCreatedAt(), cycleDays));
        return logVo;
    }

    private boolean startNewCycle(
            UserSignInLogEntity firstInOneCycle,
            UserSignInLogEntity lastInOneCycle,
                               int leftTimes,
                               int cycleDays,
                               String userUuid,
                               UserSignInLogVo logVo,
                               Date today){
        Date firstSignInAt = firstInOneCycle.getCreatedAt();
        //剩余可签到天数
        int leftDays = DateUtil.getDaysBetween(today,DateUtil.addDays(firstSignInAt,cycleDays));
        boolean newCycle = leftDays < leftTimes;
        if(newCycle){
            lastInOneCycle.setCycleType(ESignInCycleType.END);
            lastInOneCycle.setStatus(-1);//标志此end是个非正常end
            userSignInLogDao.updateById(lastInOneCycle);
            saveSigninLog(userUuid, today,ESignInCycleType.IN);
            logVo.setSignedTimes(1);
            logVo.setCompletedAt(DateUtil.truncateToDay(today, cycleDays));
        }
        return newCycle;
    }

    //保存签到日志
    private void saveSigninLog(String userUuid,Date date,ESignInCycleType cycleType){
        UserSignInLogEntity userSignInLog = new UserSignInLogEntity();
        userSignInLog.setUserUuid(userUuid);
        userSignInLog.setCreatedAt(date);
        userSignInLog.setSignInAt(DateUtil.formatDate(date));
        userSignInLog.setCycleType(cycleType);
        userSignInLogDao.insert(userSignInLog);
    }
}
